(算法)扔棋子
题目:
1、有一个100层高的大厦,你手中有两个相同的玻璃围棋子。从这个大厦的某一层及更高的层扔下围棋子就会碎,用你手中的这两个玻璃围棋子,找出一个最优的策略(扔最少的次数),来得知那个临界层面。
2、如果大厦高度是N层,你有K个棋子,请问最少需要扔几次可以知道得临界层?
思路:
1、推导
这里不推倒,直接给出结论,具体推导参考:http://blog.csdn.net/jiaomeng/article/details/1435226
首先选择第x层扔第一个棋子q1:
如果棋子碎了,则在1~x-1之间通过另一个棋子q2来从下到上依次试探哪一层为临界层面;
如果棋子没碎,则选择在x+(x-1)=2x-1层继续扔棋子q1:
如果棋子碎了,则在x+1~2x之间通过另一个棋子q2来从下到上依次试探哪一层为临界层面;
如果棋子没碎,则选择在x+(x-1)+(x-2)=3x-3继续扔棋子q1;
。。。。。。
如果棋子一直没碎,那么棋子q1会一直扔到某一层大于或等于100即停止:x+(x-1)+(x-2)+(x-3)+...+2+1>=100,即求x*(x+1)/2的最小值,使之满足>=100,求解得到x=14.
即:
先从14层扔(碎了试1-13,需1+13-1+1=14次)
再从27层扔(碎了试15-26,需2+26-15+1=14次)
再从39层扔(碎了试28-38,需3+38-28+1=14次)
再从50层扔(碎了试40-49,需4+49-40+1=14次)
再从60层扔(碎了试51-59,需5+59-51+1=14次)
再从69层扔(碎了试61-68,需6+68-61+1=14次)
再从77层扔(碎了试70-76,需7+76-70+1=14次)
再从84层扔(碎了试78-83,需8+83-78+1=14次)
再从90层扔(碎了试85-89,需9+89-85+1=14次)
再从95层扔(碎了试91-94,需10+94-91+1=14次)
再从99层扔(碎了试96-98,需11+98-96+1=14次)
最后从100层扔(根据题意一定会碎,也可以不扔了,需11次)
最坏情况下扔14次。
2、动态规划
假设dp[i][j]表示有i层,j个棋子时得到临界层,需要的最少次数。
初始状态:
当i=1,即只有1层,dp[1][j]=1;(j>0)
当j=1,只有1个棋子,dp[i][1]=i;(i>0)
状态转移方程:
dp[i][j]=min(max(dp[k-1][j-1],dp[i-k][j])+1) (0<k<i)
解释:当选择在第k层扔棋子时,此时有两种情况,考虑的最坏情况,因此选择其大者。
棋子碎了,则棋子数减1,并在1~k-1层试探,此时次数为dp[k-1][j-1]+1;
棋子未碎,则棋子数不变,在k+1~j层试探,此时次数为dp[j-k][j]+1;
代码:
route[i][j]表示当有i层j个棋子的下一步选择哪一层扔棋子,通过route可以记录每次扔棋子的楼层。
参考:http://blog.csdn.net/taylor_tao/article/details/7084467?reload
#include <iostream> #include <vector> #include <stdlib.h> using namespace std; #define LAYERS 101 #define CUPS 3 int main() { vector<vector<int> > dp(LAYERS,vector<int>(CUPS)); vector<vector<int> > route(LAYERS,vector<int>(CUPS)); for(int i=1;i<LAYERS;i++){ // dp[i][0]=0; dp[i][1]=i; } for(int i=1;i<CUPS;i++){ // dp[0][i]=0; dp[1][i]=1; route[1][i]=0; } for(int j=2;j<CUPS;j++){ for(int i=2;i<LAYERS;i++){ dp[i][j]=i; route[i][j]=0; for(int k=1;k<i;k++){ int mini=max(dp[k-1][j-1],dp[i-k][j])+1; route[i][j]=mini<=dp[i][j]?k:route[i][j]; dp[i][j]=mini<dp[i][j]?mini:dp[i][j]; } } } cout << dp[LAYERS-1][CUPS-1] <<endl; return 0; }